package com.gitee.easyopen.register;


import com.gitee.easyopen.util.FieldUtil;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author tanghc
 */
public class SingleParameterWrapper implements Opcodes {

    private static final Logger logger = LoggerFactory.getLogger(SingleParameterWrapper.class);

    private static final String WrapperParam = "WrapperParam";

    private static final String PKG = "com/gitee/easyopen/gen/";
    private static final String OBJECT = "java/lang/Object";

    private static final MyClassLoader CLASS_LOADER = new MyClassLoader();

    private static AtomicInteger i = new AtomicInteger();

    /**
     * 生成一个类，里面指放这个字段
     *
     * @param parameter 字段，只能是基本类型或字符串类型
     * @return 如果不是基本类型或字符串类型，返回null
     */
    public Class<?> wrapParam(Parameter parameter, String paramName) {
        Class<?> paramType = parameter.getType();
        if (!FieldUtil.isNumberOrStringType(paramType)) {
            return null;
        }
        /********************************class***********************************************/
        // 创建一个ClassWriter, 以生成一个新的类
        ClassWriter cw = new ClassWriter(0);
        String className = WrapperParam + i.incrementAndGet();
        cw.visit(V1_8, ACC_PUBLIC, PKG + className, null, OBJECT, null);

        /*********************************constructor**********************************************/
        MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null,
                null);
        mw.visitVarInsn(ALOAD, 0);
        mw.visitMethodInsn(INVOKESPECIAL, OBJECT, "<init>", "()V");
        mw.visitInsn(RETURN);
        mw.visitMaxs(1, 1);
        mw.visitEnd();


        /*************************************parameter******************************************/
        //生成String name字段
        Type type = Type.getType(paramType); // Ljava/lang/String;
        FieldVisitor fv = cw.visitField(ACC_PUBLIC, paramName, type.getDescriptor(), null, null);
        // 生成验证注解
        Annotation[] annotations = AnnotationUtils.getAnnotations(parameter);
        for (Annotation annotation : annotations) {
            ValidationAnnotationDefinition validationAnnotationDefinition = ValidationAnnotationDefinitionFactory.build(annotation);
            if (validationAnnotationDefinition == null) {
                continue;
            }
            Class<?> annoClass = validationAnnotationDefinition.getAnnotationClass();
            Type annoType = Type.getType(annoClass);
            AnnotationVisitor av = fv.visitAnnotation(annoType.getDescriptor(), true);
            Map<String, Object> properties = validationAnnotationDefinition.getProperties();
            if (properties != null) {
                try {
                    Set<Map.Entry<String, Object>> entrySet = properties.entrySet();
                    for (Map.Entry<String, Object> entry : entrySet) {
                        Object val = entry.getValue();
                        if (val != null) {
                            // 设置枚举值
                            if (val.getClass().isEnum()) {
                                Type eType = Type.getType(val.getClass());
                                av.visitEnum(entry.getKey(),eType.getDescriptor(), val.toString());
                            } else if(val instanceof Class<?>){
                                // val是Class类型
                                Type vType = Type.getType((Class<?>)val);
                                av.visit(entry.getKey(), vType);
                            }else {
                                av.visit(entry.getKey(), val);
                            }
                        }
                    }
                } catch (Exception e) {
                    logger.error("ASM生成注解出错", e);
                }
            }
            // 结束生成注解
            av.visitEnd();
        }
        fv.visitEnd();

        /***********************************generate and load********************************************/
        byte[] code = cw.toByteArray();

//        try {
//            //将二进制流写到本地磁盘上
//            FileOutputStream fos = new FileOutputStream("D:/" + className + ".class");
//            //写文件
//            fos.write(code);
//            //关闭输出流
//            fos.close();
//        } catch (Exception e) {
//        }

        Class<?> clazz = CLASS_LOADER.defineClass(code);
        logger.info("生成参数包装类：{}，包装参数名：{}，参数类型：{}", clazz.getName(), paramName, paramType);
        return clazz;
    }

    private static String formatDot(String packageName) {
        return packageName.replace(".", "/");
    }

    static class MyClassLoader extends ClassLoader {
        public Class<?> defineClass(byte[] clazz) {
            return this.defineClass(null, clazz, 0, clazz.length);
        }
    }

}
